﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.DirectoryServices;
using System.Web.Security;
using System.Web.Profile;
using System.Xml.Linq;
using DataLayer;

/// <summary>
/// Summary description for Membership
/// </summary>
public class NexusMembershipProvider : System.Web.Security.SqlMembershipProvider
{
   public NexusMembershipProvider()
   {
   }

   public override bool ValidateUser(string username, string password)
   {
      LdapUser UserInfo = new LdapUser(username);
      if (!UserInfo.isValid)
         return false;

      string LDAPPATH = "LDAP://" + UserInfo.LdapServer;

      DirectoryEntry entry = new DirectoryEntry(LDAPPATH, (UserInfo.isSSO || UserInfo.isISSO ? Settings.LdapUserID : UserInfo.UserID), (UserInfo.isSSO || UserInfo.isISSO ? Settings.LdapUserPassword : password));
      try
      {
         object obj = entry.NativeObject;
         MembershipUser user = Membership.GetUser(UserInfo.UserName);
         if (user == null)
         {
            using (DirectorySearcher search = new DirectorySearcher(entry))
            {
               search.Filter = string.Format("(samAccountName={0})", UserInfo.UserID);
               SearchResult SRresult = search.FindOne();
               using (DirectoryEntry de = SRresult.GetDirectoryEntry())
               {
                  string Email = GetProperty(de, "mail");
                  user = Membership.CreateUser(UserInfo.UserName, "P@ssw0rd1", (Email.Length > 0 ? Email : UserInfo.UserID + "_" + DateTime.Now.Ticks + "@nus.edu.sg"));
                  if (user != null)
                  {
                     #region Initialize User Profile
                     ProfileCommon pc = (ProfileCommon)ProfileBase.Create(UserInfo.UserName);
                     pc.IsFirstVisit = true;
                     pc.Fullname = GetProperty(de, "DisplayName").TitleCase();
                     pc.EnableHints = true;
                     pc.Referrer = "";
                     pc.Save();
                     #endregion

                     #region New User

                     #region Load Initialize XML
                     XElement doc = null;
                     HttpContext Current = HttpContext.Current;
                     HttpServerUtility Server = Current.Server;
                     switch (UserInfo.UserLdapType)
                     {
                        case UserType.NUSAlumni:
                           Roles.AddUserToRoles(UserInfo.UserName, "Everyone,NUS,NUS-Alumni".Split(",".ToCharArray()));
                           doc = XElement.Load(Server.MapPath("/template/nusalm.xml"));
                           break;
                        case UserType.NUSStaff:
                           Roles.AddUserToRoles(UserInfo.UserName, "Everyone,NUS,NUS-Staff".Split(",".ToCharArray()));
                           doc = XElement.Load(Server.MapPath("/template/nusstf.xml"));
                           break;
                        case UserType.NUSStudent:
                           Roles.AddUserToRoles(UserInfo.UserName, "Everyone,NUS,NUS-Student".Split(",".ToCharArray()));
                           doc = XElement.Load(Server.MapPath("/template/nusstu.xml"));
                           break;
                     }
                     #endregion

                     if (doc != null)
                     {
                        bool addedUserSetting = false;
                        int lastTabId = 0;
                        byte order = 1;
                        using (var db = DatabaseHelper.GetDashboardData())
                        {
                           Guid UserGuid = Functions.GetUserProviderKey(UserInfo.UserName);
                           foreach (var xTab in doc.Elements("Tab"))
                           {
                              var T = new Tab();
                              T.UserId = UserGuid;
                              T.LastUpdate = DateTime.Now;
                              T.CreatedDate = DateTime.Now;
                              T.Icon = "";
                              T.PanelWidth_1 = Typecast.ToByte(xTab.Attribute("C1").Value);
                              T.PanelWidth_2 = Typecast.ToByte(xTab.Attribute("C2").Value);
                              T.PanelWidth_3 = Typecast.ToByte(xTab.Attribute("C3").Value);
                              T.PanelWidth_4 = Typecast.ToByte(xTab.Attribute("C4").Value);
                              T.Title = xTab.Attribute("title").Value;
                              T.InnerHTML = xTab.Attribute("innerhtml").Value;
                              DatabaseHelper.Insert<Tab>(T);

                              //add in the usertab table
                              var UT = new UserTab();
                              UT.AccessLevel = 0;
                              UT.Order = order++;
                              UT.TabId = T.ID;
                              UT.UserID = UserGuid;
                              UT.RoleID = Constants.Role_Individual;
                              DatabaseHelper.Insert<UserTab>(UT);

                              //must add in one of these
                              if (xTab.Attribute("isActive").Value == "1")
                              {
                                 var xSetting = new UserSetting();
                                 xSetting.ActiveTab = T.ID;
                                 xSetting.UserId = UserGuid;
                                 xSetting.LoginCount = 0;
                                 DatabaseHelper.Insert<UserSetting>(xSetting);
                                 addedUserSetting = true;
                              }

                              //now insert the widgets for column 1, column 2, etc
                              var idx = 1;
                              for (int i = 1; i < 5; i++)
                              {
                                 idx = 1;
                                 foreach (var xWidget in xTab.Elements("C" + i).Elements("Widget"))
                                 {
                                    var theWidget = db.Widgets.Single(p => p.Name == xWidget.Attribute("name").Value.ToLower());

                                    if (theWidget != null)
                                    {
                                       //widget is found, add it to this column
                                       var iWidget = new TabWidget();
                                       iWidget.ColumnNo = i;
                                       iWidget.CreatedDate = DateTime.Now;
                                       iWidget.Expanded = true;
                                       iWidget.LastUpdate = DateTime.Now;
                                       iWidget.OrderNo = idx++;
                                       iWidget.State = theWidget.DefaultState;
                                       iWidget.TabId = T.ID;

                                       if (xWidget.Attribute("icon") != null)
                                       {
                                          iWidget.Icon = xWidget.Attribute("icon").Value;
                                       }

                                       if (xWidget.Attribute("state") != null)
                                       {
                                          iWidget.State = xWidget.Attribute("state").Value;
                                       }

                                       if (xWidget.Attribute("title") != null)
                                       {
                                          iWidget.Title = xWidget.Attribute("title").Value;
                                       }
                                       else
                                       {
                                          iWidget.Title = theWidget.Name;
                                       }
                                       iWidget.WidgetId = theWidget.ID;
                                       DatabaseHelper.Insert<TabWidget>(iWidget);
                                    }
                                 }
                              }

                              lastTabId = T.ID;
                           }

                           if (!addedUserSetting)
                           {
                              var xSetting = new UserSetting();
                              xSetting.ActiveTab = lastTabId;
                              xSetting.UserId = UserGuid;
                              DatabaseHelper.Insert<UserSetting>(xSetting);
                           }

                           //Check if this guy has any roletabs
                           var RTs = from p in db.aspnet_UsersInRoles
                                     join T in db.RoleTabs on p.RoleId equals T.RoleID
                                     where p.UserId == UserGuid
                                     select new { T.TabId, T.RoleID, T.AccessLevel };
                           foreach (var RT in RTs)
                           {
                              var UT = new UserTab();
                              UT.AccessLevel = RT.AccessLevel;
                              UT.Order = order++;
                              UT.TabId = RT.TabId;
                              UT.UserID = UserGuid;
                              UT.RoleID = RT.RoleID;
                              DatabaseHelper.Insert<UserTab>(UT);
                           }
                        }
                     }
                     #endregion
                  }
               }
            }
         }
         Log_Login(UserInfo.UserName, HttpContext.Current.Request, true, UserInfo.isISSO);
         return true;
      }
      catch (Exception err)
      {
         HttpContext.Current.Session["Err"] = err.ToString();
      }

      Log_Login(UserInfo.UserID, HttpContext.Current.Request, false, UserInfo.isISSO);
      return false;
   }

   /// <summary>
   /// Gets the User Password Last Set Date from LDAP
   /// </summary>
   /// <param name="UserID"></param>
   /// <returns></returns>
   public static DateTime GetUserPasswordExpiry(string UserID)
   {
      try
      {
         LdapUser UserInfo = new LdapUser(UserID);
         if (!UserInfo.isValid)
            return new DateTime(1, 1, 1);

         string LDAP = UserInfo.LdapServer;

         DirectoryEntry entry = new DirectoryEntry("LDAP://" + LDAP, Settings.LdapUserID, Settings.LdapUserPassword);
         //Bind to the native AdsObject to force authentication.			
         Object obj = entry.NativeObject;

         DirectorySearcher search = new DirectorySearcher(entry);
         search.Filter = string.Format("(samAccountName={0})", UserInfo.UserID);
         search.PropertiesToLoad.Add("cn");
         System.DirectoryServices.SearchResult SRresult = search.FindOne();

         try
         {
            DirectorySearcher ds = new DirectorySearcher(
              SRresult.GetDirectoryEntry(),
              "(pwdLastSet=*)",
              new string[] { "pwdLastSet" },
              SearchScope.Base
              );

            System.DirectoryServices.SearchResult sr = ds.FindOne();
            if (sr != null)
            {
               if (sr.Properties.Contains("pwdLastSet"))
               {
                  long pwdLastSet = (Int64)sr.Properties["pwdLastSet"][0];
                  if (pwdLastSet > 0)
                  {
                     return DateTime.FromFileTime(pwdLastSet);
                  }
                  else
                     return new DateTime(1, 1, 1);
               }
            }
         }
         catch
         {
         }
      }
      catch
      {
      }
      return new DateTime(1, 1, 1);
   }
   public static string GetLdapServerFromUserType(UserType UserLdapType)
   {
      switch (UserLdapType)
      {
         case UserType.NUSStudent:
            return "ldapstu.nus.edu.sg";
         case UserType.NUSAlumni:
            return "137.132.4.26";
         case UserType.NUSStaff:
            return "ldapstf.nus.edu.sg";
      }
      return "";
   }
   #region Increment the Login Count
   public static void Log_Login(string UserName, HttpRequest Request, bool Result)
   {
      Log_Login(UserName, Request, Result, false);
   }
   public static void Log_Login(string UserName, HttpRequest Request, bool Result, bool isSimulate)
   {
      EntLib.EntLibHelper.Log_Login((isSimulate ? "isso_" + UserName : UserName), Request.UserAgent, Result);
      try
      {
         if (!isSimulate)
         {
            using (var db = DatabaseHelper.GetDashboardData())
            {
               db.ExecuteCommand("update UserSetting set LoginCount=LoginCount+1 where UserID={0}", Functions.GetUserProviderKey(UserName));
            }
         }
      }
      catch
      {
      }
   }
   #endregion

   #region Get Ldap Properties
   public static string GetLdapProperty(string UserName, string Property)
   {
      LdapUser UserInfo = new LdapUser(UserName);
      return GetLdapProperty(UserInfo, Property);
   }
   public static string GetLdapProperty(LdapUser UserInfo, string Property)
   {
      if (!UserInfo.isValid)
         return "";

      string LDAPPATH = "LDAP://" + UserInfo.LdapServer;

      if (LDAPPATH.Length > 0)
      {
         DirectoryEntry entry = new DirectoryEntry(LDAPPATH, Settings.LdapUserID, Settings.LdapUserPassword);
         try
         {
            object obj = entry.NativeObject;

            using (DirectorySearcher search = new DirectorySearcher(entry))
            {
               search.Filter = string.Format("(samAccountName={0})", UserInfo.UserID);
               SearchResult SRresult = search.FindOne();
               using (DirectoryEntry de = SRresult.GetDirectoryEntry())
               {
                  return GetProperty(de, Property);
               }
            }

         }
         catch
         {
         }
      }
      return "";
   }
   public static string GetProperty(DirectoryEntry de, string property)
   {
      try
      {
         return de.Properties[property].Value.ToString();
      }
      catch (Exception)
      {
         return "";
      }
   }
   #endregion

   #region Throw exception for functions that are not supposed to work
   public override bool ChangePassword(string username, string oldPassword, string newPassword)
   {
      throw new NotImplementedException();
   }

   public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
   {
      throw new NotImplementedException();
   }

   public override string GetPassword(string username, string answer)
   {
      throw new NotImplementedException();
   }
   #endregion
}

public enum UserType
{
   NUSStaff = 0,
   NUSStudent = 1,
   Guest = 2,
   NUSAlumni = 3
}

public struct LdapUser
{
   public string UserID;
   public UserType UserLdapType;
   public bool isSSO;
   public bool isISSO;
   public string LdapServer;
   public bool isValid;
   public string UserName;

   public LdapUser(string UserName)
   {
      /*
      * UserID format: 
       * domain\username|sso for single sign on, isso for automated single sign on
      */

      isISSO = isSSO = isValid = false;
      UserID = LdapServer = this.UserName = "";
      UserLdapType = UserType.Guest;

      if (UserName.Length > 0)
      {
         int SlashIdx = UserName.IndexOf('\\');

         if (SlashIdx > -1)
         {
            switch (UserName.Substring(0, SlashIdx).ToLower())
            {
               case "nusstf": UserLdapType = UserType.NUSStaff; this.UserName = "nusstf\\"; break;
               case "nusstu": UserLdapType = UserType.NUSStudent; this.UserName = "nusstu\\"; break;
               case "nusalm": UserLdapType = UserType.NUSAlumni; this.UserName = "nusalm\\"; break;
            }

            UserName = UserName.Substring(SlashIdx + 1);
         }

         LdapServer = NexusMembershipProvider.GetLdapServerFromUserType(UserLdapType);

         SlashIdx = UserName.IndexOf("|");
         if (SlashIdx > -1)
         {
            switch (UserName.Substring(SlashIdx + 1).ToLower())
            {
               case "sso":
                  isSSO = true;
                  break;
               case "isso":
                  isISSO = true;
                  break;
            }
            UserName = UserName.Substring(0, SlashIdx);
         }
         UserID = UserName;
      }

      isValid = UserLdapType != UserType.Guest && UserID.Length > 0;
      this.UserName += UserID;
   }
}